/** MeterServ - CGI program to query the meter readings database produced
 * by MeterMon.
 *
 * The program takes its input from the URL calling it. The main parameter
 * is 'mode', which specifies the sort of output required.  
 * Valid modes are:
 *
 * summary : print a summary of number of meters in the database, and their
 *            properties.
 */

#include "meter.h"
#include <stdio.h>
#include <gcgi.h>
#include <string.h>
#include <sqlite3.h>
#include <gdc.h>
#include <gdchart.h>

#define FMT_HTML 0
#define FMT_TEXT 1



void write_summary();
void write_alldata();
void graph_alldata();
void read_alldata();
void settime();
void startstop();
void configure();
void timet_to_str();

/* Initialise global variables */
int n_meter;
meterinfo meters[MAXMETER];

#define MAX_POINTS 10000
#define LABEL_LENGTH 10
time_t timeval[MAX_POINTS];                       /* meter data times */
float data[MAX_POINTS];                           /* meter data values */
float rate[MAX_POINTS];
char   **labels;          
int num_points=0;                                 /* number of points in the timeval and data arrays */
float maxval, minval, maxrate, minrate;           /* maximum and minimum meter values and consumption rates */
int n_meter;
meterinfo meters[MAXMETER];



int main() {
  int n_fields=0;
  char *data;
  char mode[30];


  char formatstr[30];
  int format = FMT_HTML;


  if (initCgi() < 0 ) {
    printf("Error initialising gcgi\n\n");
  }

  /* Read default meter configuration from database */
  n_meter=0;
  n_meter = db_initialise_meters();

  printf("Content-type: text/html\n\n");

  if (gcgiFetchString("format",formatstr,30)==GCGIFIELDNOTFOUND) {
    //    printf("Format not specified - using FMT_HTML<br>\n");
    format = FMT_HTML;
  } else {
    if (!strcmp(formatstr,"text")) {
      format = FMT_TEXT;
    } else if (!strcmp(formatstr,"html")) {
      format = FMT_HTML;
    }
  }

  if (format==FMT_HTML) {
    /*    printf("Content-type: text/html\n\n");*/
    printf("<html><head>\n");
    printf("<title>MeterServ</title>\n");
    printf("</head>\n");
    printf("<body>\n");
  }
  else {
    printf("Content-type: text\n\n");
  }

  gcgiNumFormFields(&n_fields);
  //printf("n_fields=%d<br>\n",n_fields);
  if (gcgiFetchString("mode",mode,30)==GCGIFIELDNOTFOUND) {
    printf("Error - Mode not specified\n");
  } else if (!strcmp(mode,"summary")) {
    //printf("mode=summary<br>\n");
    write_summary();
  } else if (!strcmp(mode,"alldata")) {
    //printf("mode=alldata<br>\n");
    write_alldata();
  } else if (!strcmp(mode,"graph")) {
    //printf("mode=graph<br>\n");
    graph_alldata();
  } else if (!strcmp(mode,"settime")) {
    //printf("mode=settime<br>\n");
    settime();
  } else if (!strcmp(mode,"configure")) {
    //printf("mode=configure<br>\n");
    configure();
  } else if (!strcmp(mode,"startstop")) {
    //printf("mode=startstop<br>\n");
    startstop();
  } else {
    printf("unrecognised mode=%s<br>\n",mode);
  }
  freeCgi();

  
  if (format==FMT_HTML) {
    printf("</body></html>\n");
  }
  return(0);
}


void write_summary() {
  int i;
  int metno=-1; //=all meters
  //  n_meter = 0;
  // n_meter = db_initialise_meters();
  
  gcgiFetchInteger("meter",&metno,-1);
  if ((metno<-1) || (metno>n_meter-1))
    printf("Error - Meter Number %d out of range<br>\n",
	   metno);
  else
    if (metno==-1) {
      /* Produce summary of all meters */
      printf("<h1>MeterServ - Summary of All Meters</h1>\n");
      printf("<table border=\"1\">\n");
      printf("<tr>\n");
      printf("<td>Meter No.</td>\n");
      printf("<td>Meter Description</td>\n");
      printf("<td>Reading</td>\n");
      printf("<td>IO Card</td>\n");
      printf("<td>Input</td>\n");
      printf("</tr>\n");
      for (i=0;i<n_meter;i++)
	{
	  printf("<tr>\n");
	  printf("<td><a href=\"meterserv?mode=summary&meter=%d\">%d</a></td>\n",i,i);
	  printf("<td>%s</td> <td>%.2lf</td> <td>%d</td> <td>%d</td>\n",
		 meters[i].description,
		 meters[i].reading,
		 meters[i].iocard,
		 meters[i].input
		 );
	  printf("</tr>\n");
	}
    }
    else 
      {
	/* Write Single Meter Summary */
	printf("<h1>Summary for Meter \"%s\"<h1>\n",
	       meters[metno].description);
	printf("<table border=\"1\">\n");
	printf("<tr>\n");
	printf("<td><b>Parameter</b></td>\n");
	printf("<td><b>Value</b></td>\n");
	printf("<tr>\n");
	
	printf("<tr><td>Meter Number</td><td>%d</td></tr>\n",metno);
	printf("<tr><td>Description</td><td>%s</td></tr>\n",meters[metno].description);
	printf("<tr><td>Unit</td><td>%s</td></tr>\n",meters[metno].unit);
	printf("<tr><td>Output Period</td><td>%d sec</td></tr>\n",
	       meters[metno].output_period);
	printf("<tr><td>Conversion Factor (cfac)</td><td>%lf</td></tr>\n",
	       meters[metno].cfac);
	printf("<tr><td>I/O Card Number</td><td>%ld</td></tr>\n",
	       meters[metno].iocard);
	printf("<tr><td>I/O Card Input Number</td><td>%d</td></tr>\n",
	       meters[metno].input);
	printf("<tr><td>Latest Reading</td><td>%.2lf %s</td></tr>\n",
	       meters[metno].reading,meters[metno].unit);
	printf("<tr><td>Last Output Time</td><td>%ld sec</td></tr>\n",
	       meters[metno].last_output);
	printf("</table>\n");
      }
}

void write_alldata() {
  int i;

  char formatstr[30];
  char timestr[30];
  int format = FMT_HTML;

  if (gcgiFetchString("format",formatstr,30)==GCGIFIELDNOTFOUND) {
      printf("Format not specified - using FMT_HTML<br>\n");
      format = FMT_HTML;
    } else if (!strcmp(formatstr,"text")) {
      printf("format=text\n");
      format = FMT_TEXT;
    } else if (!strcmp(formatstr,"html")) {
      printf("format=html<br>\n");
      format = FMT_HTML;
    }

  read_alldata();

  printf("Number of Data Points = %d\n<br>",num_points);
  for(i=0;i<num_points;i++) {
    timet_to_str(timeval[i],timestr);
    printf("%s\t%7.1f\t%4.2f\n<br>",timestr,data[i],rate[i]);
  }

}



/* draw a graph of all of the data in the database for the meter number specified on the CGI command line
 * The following CGI parameters are recognised:
 *   meter=<metno> - meter number
 *   sdate=<sdate> - start date (dd/mm/yyyy)
 *   edate=<edate> - end date (dd/mm/yyyy)
 *   period=<period> - period to select data:  1=all data, 2=daily, 3=monthly, 4=yearly
 * uses the gdchart library for the graph drawing.
 * 29apr2006  GJ  ORIGINAL VERSION
 * 11oct2006  GJ  Updated to use read_alldata procedure for data extraction.
*/
void graph_alldata() {
  int rc;
  int i;
  FILE *op;

  /* Read data from database into global variables */
  read_alldata();

  op=fopen("/var/www/images/graph.png","w");
  if (op==NULL) {
    printf("Error opening output file, graph.png<br>\n");
  }
  else {
    /* Produce X axis labels */
    /*char *labeldata;
    int n;
    fprintf(stderr,"Allocating labeldata<br>\n");
    labeldata =  (char*)malloc(num_points*LABEL_LENGTH+2);
    fprintf(stderr,"Allocating **labels<br>\n");
    labels = (char**)malloc(num_points*sizeof(char**));
    fprintf(stderr,"Initialising labels array<br>\n");
    for (n=0;n<num_points;n++) {
      labels[n] = &labeldata[n*LABEL_LENGTH];
    }
    fprintf(stderr,"Finished initialising labels array<br>\n");
    for (i=0;i<num_points;i++) {
      sprintf(&labels[i][0],"%d",i);
      }
    */


    GDC_BGColor   = 0xFFFFFFL;                  /* backgound color (white) */
    GDC_LineColor = 0x000000L;                  /* line color      (black) */
    GDC_image_type = GDC_PNG;
    GDC_requested_ymin = 0.;
    GDC_ylabel_fmt = "%.2f";
    GDC_out_graph( 700, 350,      /* short       width, height */
               op,        /* FILE*       open FILE pointer */
               GDC_LINE,     /* GDC_CHART_T chart type */
               num_points,             /* int         number of points per data set */
		   NULL,             /* char*[]     array of X labels */
               1,             /* int         number of data sets */
               (float*)rate, NULL );             /* float[]     data set 1 */


    fclose(op);

    printf("<img src=\"/images/graph.png\" alt=\"/images/graph.png\"><br>\n");
  }
}



void startstop() {
  printf("startstop\n");
  system("/bin/ps");


}


/***************************************************************
 * Presents a web page to allow a specified meter to be configured
 * If the submit button on the page is pressed, the data is written
 * to the database
 * 02mar2006  GJ  ORIGINAL VERSION
 * 21oct2006  GJ  Modified to read all data as strings, since gcgiFetchDouble does not seem to work.
 */
void configure() {
  int metno,submitval,output_period, retval;
  char description[20];
  char unit[20];
  char cfacstr[20], readingstr[20], periodstr[20];
  double cfac, reading;

  //  printf("configure()<br>\n");
  gcgiSaveEnvVariables("urlencodedenv"); 
  if (gcgiFetchInteger("meter",&metno,0)==GCGIFIELDNOTFOUND) {
    printf("Error - meter not specified\n");
    return;
  }

  /*
   * If this program is called with a parameter submit=1, we write data to the database,
   * otherwise we just present a form to edit the data
   */
  if ((gcgiFetchInteger("submit",&submitval,0)!=GCGIFIELDNOTFOUND) 
      && (submitval==1) ) { 
    //printf("configure() - writing data to database\n<br>");
    if (gcgiFetchString("description",meters[metno].description,20)==GCGIFIELDNOTFOUND) {
      printf("description not specified<br>\n");
    }
    if (gcgiFetchString("unit",meters[metno].unit,10)==GCGIFIELDNOTFOUND) {
      printf("unit not specified<br>\n");
    }
    if ((retval=gcgiFetchString("cfac",cfacstr,20))!=GCGISUCCESS) {
      printf("cfac not specified - retval=%d<br>\n",retval);
    }
  else {
    sscanf(cfacstr,"%lf",&(meters[metno].cfac));
    //printf("cfacstr=%s, cfac=%lf<br>\n",cfacstr,meters[metno].cfac);
  }
    if (gcgiFetchString("reading",readingstr,20)==GCGIFIELDNOTFOUND) {
      printf("reading not specified<br>\n");
    }
    else {
      sscanf(readingstr,"%lf",&(meters[metno].reading));
      //printf("readingstr=%s, reading=%lf<br>\n",readingstr,meters[metno].reading);
    }
    
   
    if (gcgiFetchString("output_period",periodstr,20)==GCGIFIELDNOTFOUND) {
      printf("output_period not specified<br>\n");
    }
    else {
      sscanf(periodstr,"%ld",&(meters[metno].output_period));
      //printf("periodstr=%s, output_period=%ld<br>\n",periodstr,meters[metno].output_period);
    }
    
    //    printf("Writing the following data to the database:<br>\n");
    //printf("Meter Number = %d<br>\n",metno);
    //printf("Description = %s<br>\n",meters[metno].description);
    //printf("Unit = %s<br>\n",meters[metno].unit);
    //printf("Cfac = %lf<br>\n",meters[metno].cfac);
    //printf("Reading = %lf<br>\n",meters[metno].reading);
    //printf("Output Period = %ld<br>\n",meters[metno].output_period);
    //printf("<hl>\n");
		
    retval = db_save_meter_data(metno);

    if (retval!=0) {
      printf("<b>ERROR</b> - Failed to Write to Database<br>\n");
    }
    else {
      printf("Data Written to Database OK<br><br>\n");
    }
  } 

  printf("<h1>Configuring Meter Number %d</h1>\n",metno);
  printf("<p>Update the meter data in the form below.  Press the Submit button to update the system.</p>\n");
  printf("<form method=\"GET\" action=\"meterserv\">\n");
  printf("<table border=\"1\">\n");
  printf("<tr>\n");
  printf("<td>Meter Number</td><td> <input name=\"meter\" type=\"text\" value=\"%02d\" width=\"3\"></td><br>\n",
	 metno);
  printf("</tr><tr>\n");
  printf("<td>Description:</td><td> <input name=\"description\" type=\"text\" value=\"%s\" width=\"3\"></td><br>\n",
	 meters[metno].description);
  printf("</tr><tr>\n");
  printf("<td>Unit: </td><td><input name=\"unit\" type=\"text\" value=\"%s\" width=\"3\"></td><br>\n",
	 meters[metno].unit);
  printf("</tr><tr>\n");
  printf("<td>Cfac:</td><td> <input name=\"cfac\" type=\"text\" value=\"%lf\" width=\"3\"></td><br>\n",
	 meters[metno].cfac);
  printf("</tr><tr>\n");
  printf("<td>Reading:</td><td> <input name=\"reading\" type=\"text\" value=\"%lf\" width=\"5\"></td><br>\n",
	 meters[metno].reading);
  printf("</tr><tr>\n");
  printf("<td>Output Period: </td><td><input name=\"output_period\" type=\"text\" value=\"%d\" width=\"5\"></td><br>\n",
	 meters[metno].output_period);
  printf("</tr>\n");
  printf("</table>\n");
  printf("<input type=\"submit\">\n");
  printf("<input name=\"mode\" value=\"configure\" type=\"hidden\">\n");
  printf("<input type=\"hidden\" name=\"submit\" value=\"1\">\n");
  printf("</form>\n");

}

void settime() {
  time_t tnow;
  struct tm *tmstr;
  int timevalid=1, datevalid=1;
  int hour, minute, day,month,year;

  if (gcgiFetchInteger("hour",&hour,0)==GCGIFIELDNOTFOUND) {
    tnow = time(NULL);
    tmstr = localtime(&tnow);
    
    printf("%02d:%02d:%02d %02d/%02d/%04d<br>\n",
	   tmstr->tm_hour,
	   tmstr->tm_min,
	   tmstr->tm_sec,
	   tmstr->tm_mday,
	   tmstr->tm_mon+1,
	   tmstr->tm_year+1900);
    
    printf("<form method=GET action=\"meterserv?mode=settime\">\n");
    printf("<input name=\"mode\" value=\"settime\" type=text readoonly><br>\n");
    printf("Time: <input name=\"hour\" type=text value=%02d width=3>:",
	   tmstr->tm_hour);
    printf("<input name=\"minute\" type=text value=%02d width=3><br>\n",
	   tmstr->tm_min);
    printf("Date: <input name=\"day\" type=text value=%02d width=3>/",
	   tmstr->tm_mday);
    printf("<input name=\"month\" type=text value=%02d width=3>/",
	   tmstr->tm_mon+1);
    printf("<input name=\"year\" type=text value=%02d width=5><br>\n",
	   tmstr->tm_year+1900);
    printf("<input type=submit>\n");
  }
  else
    {
      if (gcgiFetchInteger("hour",&hour,0)==GCGIFIELDNOTFOUND) timevalid=0;
      if (gcgiFetchInteger("minute",&minute,0)==GCGIFIELDNOTFOUND) timevalid=0;
      if (gcgiFetchInteger("day",&day,0)==GCGIFIELDNOTFOUND) datevalid=0;
      if (gcgiFetchInteger("month",&month,0)==GCGIFIELDNOTFOUND) datevalid=0;
      if (gcgiFetchInteger("year",&year,0)==GCGIFIELDNOTFOUND) datevalid=0;
  
      if (timevalid) {

      }

      if (datevalid) {

      }

    }
}



/** 
 * Callback function used by sqlite3 database functions - called by read_alldata
 */
static int read_alldata_callback(void *NotUsed, int argc, char **argv, char **azColName){
  int i;
  time_t tmt;
  struct tm *tmstr;
  double reading;
  char formatstr[30];
  int format = FMT_HTML;

  // argv[0] = meter number
  // argv[1] = time
  // argv[2] = reading
  sscanf(argv[1],"%ld",&tmt);
  sscanf(argv[2],"%lf",&reading);
  tmstr = localtime (&tmt);
  /*  printf("%ld,\t %02d:%02d:%02d\t %02d/%02d/%04d\t %10.1f",
   * tmt,
   * tmstr->tm_hour,
   * tmstr->tm_min,
   * tmstr->tm_sec,
   * tmstr->tm_mday,
   * tmstr->tm_mon+1,
   * tmstr->tm_year+1900,
   * reading);
   */
  timeval[num_points]=tmt;
  data[num_points]=reading;
  /*  sprintf(&labels[num_points][0],
   *  "%02d/%02d/%04d",
   *  tmstr->tm_mday,
   *  tmstr->tm_mon+1, 
   *  tmstr->tm_year+1900);
   */
  if (num_points<MAX_POINTS-1)
    num_points++;
  else
    printf("ERROR - Too Many Data Points - MAXPOINTS=%d<br>\n",MAX_POINTS);
  return 0;
}

/* Read all of the data in the database for the meter number specified on the CGI command line
 * The following CGI parameters are recognised:
 *   meter=<metno> - meter number
 *   sdate=<sdate> - start date (dd/mm/yyyy)
 *   edate=<edate> - end date (dd/mm/yyyy)
 *   period=<period> - period to select data:  1=all data, 2=daily, 3=monthly, 4=yearly
 * the global arrays timeval, data and rate are populated on exit,
 *  along with the variable num_points which is the number of data points
 *  read into the arrays..
 * 11oct2006  GJ  ORIGINAL VERSION based on graph_alldata from 29apr2006.
*/
void read_alldata() {
  int rc;
  sqlite3 *db;
  char *zErrMsg = 0;
  char sqlstr[256];
  int metno;
  char sdate[32];
  char edate[32];
  int period;

  /* Read CGI command line to determine query to perform */
  if (gcgiFetchInteger("meter",&metno,0)==GCGIFIELDNOTFOUND) {
    printf("Error - meter not specified\n<br>");
    return;
  }
  if (gcgiFetchInteger("period",&period,0)==GCGIFIELDNOTFOUND) {
    printf("Warning - period not specified, defaulting to 1 (all data)<br>\n");
    period=1;
  }
  if (gcgiFetchString("sdate",sdate,30)==GCGIFIELDNOTFOUND) {
      if (period!=1) printf("sdate not specified - using 01/01/1970<br>\n");
      strcpy(sdate,"01/01/1970");
  }
  if (gcgiFetchString("edate",edate,30)==GCGIFIELDNOTFOUND) {
      if (period!=1) printf("edate not specified - using 01/01/2100<br>\n");
      strcpy(edate,"01/01/2100");
  }

  printf("meter=%d, period=%d, sdate=%s, edate=%s<br>\n",
	 metno, period, sdate, edate);


  rc = sqlite3_open(DBFILE,&db);
  if( rc ){
    printf("Can't open database: %s<br>\n", sqlite3_errmsg(db));
    sqlite3_close(db);
    exit(1);
  }
  sprintf(sqlstr,"select * from readings where meter=%d",metno);
  rc = sqlite3_exec(db,sqlstr, read_alldata_callback, 0, &zErrMsg);
  if( rc!=SQLITE_OK ){
    printf("SQL error: %s\n", zErrMsg);
  }
  sqlite3_close(db);

    maxval = data[0];
    minval = data[0];
    /*    maxtime = timeval[0];
     *mintime = timeval[0];
     */
    int i;
    for(i=0;i<num_points;i++) {
      /* Calculate max and min of data */
      if (data[i] > maxval) {
	maxval = data[i];
	/*maxtime = timeval[i];*/
      }
      if (data[i] < minval) {
	minval = data[i];
        /*mintime = timeval[i];*/
      }
      /* Calculate Rate of Change of readings (in value/hour) */
      if (i==0) {
	rate[i]=0.0;
	maxrate=0.0;
	minrate=0.0;
      }
      else {
	if (timeval[i]==timeval[i-1])
	  rate[i]=0.0;
	else
	  rate[i] = 3600*(data[i]-data[i-1])/(timeval[i]-timeval[i-1]);
	if (rate[i]<minrate) minrate = rate[i];
	if (rate[i]>maxrate) maxrate = rate[i];
      }
    }
}




/**
   Converts a time in time_t (seconds from start of 1970) to the
   hh:mm dd/mm/yyy format.
   Paramters:
         time_t tnow - input time
         char *tstr  - pointer to memory for output string.
   Note: No memory is alloated for the string - the user must make
   sure that at least 17 bytes are allocated for the output string.
*/
void timet_to_str(time_t tnow, char *tstr) {
  struct tm *tmstr;
  int hour, minute, day,month,year;
  
  tmstr = localtime(&tnow);
  
  sprintf(tstr,"%02d:%02d:%02d %02d/%02d/%04d",
	  tmstr->tm_hour,
	  tmstr->tm_min,
	  tmstr->tm_sec,
	  tmstr->tm_mday,
	  tmstr->tm_mon+1,
	  tmstr->tm_year+1900);  
}
